package com.ruoyi.tokenTools;

import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.framework.web.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

//拦截器方法执行器
@Slf4j
@Component
public class LoginInterceptor extends R implements HandlerInterceptor {
    /**
     * 目标方法执行前
     * 该方法在控制器处理请求方法前执行，其返回值表示是否中断后续操作
     * 返回 true 表示继续向下执行，返回 false 表示中断后续操作
     *
     * @return
     */

    @Resource
    private TokenService tokenService;

    //    导入redis配置
    @Autowired
    @Qualifier("stringRedisTemplate")
    RedisTemplate<String,String> rt;

    //注入
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String token = tokenService.getToken(request);// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        //检查方法是否有passtoken注解，有则跳过认证，直接通过
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }

        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == ""||token==null) {
                    throw new ServletException("token为空");
                }

                // 获取 token 中的 account
                try {

                    LoginUser loginUser = tokenService.getLoginUser(request);
                    String account = loginUser.getUser().getUserName();
                    Long userId = loginUser.getUser().getUserId();
                    //取出用户id存入线程域中
                    com.test.aop.UserThreadLocal.set(userId);

                    //取出验证码
                    ValueOperations<String, String> Value = rt.opsForValue();
                    ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
                    String old_token = ops.get(account + "_token");

                    if(old_token ==null){
                        throw new ServiceException("token过期",HttpStatus.UNAUTHORIZED);
                    }
                    else if(old_token.equals(token)) {
                        return true;
                    }
                }catch (Exception e){
                    throw new ServiceException("token错误", HttpStatus.UNAUTHORIZED);
                }
            }
        }
        throw new ServiceException("token错误或过期", HttpStatus.UNAUTHORIZED);
    }


    /**
     * 目标方法执行后
     * 该方法在控制器处理请求方法调用之后、解析视图之前执行
     * 可以通过此方法对请求域中的模型和视图做进一步修改
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView
            modelAndView) throws Exception {
        System.out.println("postHandle执行{}");
    }

//    /**
//     * 页面渲染后
//     * 该方法在视图渲染结束后执行
//     * 可以通过此方法实现资源清理、记录日志信息等工作
//     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception
            ex) throws Exception {
        System.out.println("afterCompletion执行异常");
    }


}
